﻿#include <vf_pch.h>
#include <vf_apiutils.h>

namespace VFAdapter {

pt::Sketch3DPtr ApiUtils::GetFirstSketch3D() {
  pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();

  // 如无三维草图，则创建;否则在第一个三维草图上创建点
  pt::Sketches3DPtr pSketches = pPartDoc->GetSketches3D();
  // 查找得第一个不是rosette坐标系的三维草图
  pt::Sketch3DPtr pSketch = nullptr;
  for (long i = 0; i < pSketches->GetCount(); i++) {
    pt::Sketch3DPtr pTempSketch = pSketches->Item(i + 1);
    _bstr_t name = pTempSketch->GetName();
    int rosetteIdx;
    if (!sscanf_s(name, "Rosette%d", &rosetteIdx)) {
      pSketch = pTempSketch;
    }
  }
  if (pSketch == nullptr) {
    // 如示查找到，新建其
    pSketch = pSketches->Add();
  }
  return pSketch;
}

HRESULT ApiUtils::GetDispProperty(_variant_t &varRes, LPDISPATCH pObj,
                                  _bstr_t const &propName) {
  HRESULT hr = E_FAIL;
  if (nullptr == pObj)
    return E_INVALIDARG;

  try {
    DISPID rgDispId = 0;
    BSTR Names[1];
    Names[0] = propName;
    hr =
        pObj->GetIDsOfNames(IID_NULL, Names, 1, LOCALE_USER_DEFAULT, &rgDispId);
    if (FAILED(hr))
      return hr;

    DISPPARAMS disp = {NULL, NULL, 0, 0};
    hr = pObj->Invoke(rgDispId, IID_NULL, LOCALE_USER_DEFAULT,
                      DISPATCH_PROPERTYGET, &disp, &varRes, NULL, NULL);

  } catch (_com_error &e) {
    return e.Error();
  }
  return hr;
}

IDispatchPtr ApiUtils::GetParent(LPDISPATCH pObj) {
  _variant_t varRes;
  HRESULT hr = GetDispProperty(varRes, pObj, L"Parent");
  if (FAILED(hr)) {
    return hr;
  }

  if (varRes.vt != VT_DISPATCH) {
    return nullptr;
  }
  return varRes.pdispVal;
}

HRESULT ApiUtils::InvokeDelete(LPDISPATCH pGraphicDispatch) {
  HRESULT hr = NOERROR;
  try {
    if (pGraphicDispatch) {
      DISPID rgDispId = 0;
      OLECHAR name[] = L"Delete";
      OLECHAR *Names[1] = {name};

      hr = pGraphicDispatch->GetIDsOfNames(IID_NULL, Names, 1,
                                           LOCALE_USER_DEFAULT, &rgDispId);

      if (SUCCEEDED(hr)) {
        _variant_t varResult;
        DISPPARAMS disp = {NULL, NULL, 0, 0};

        hr = pGraphicDispatch->Invoke(rgDispId, IID_NULL, LOCALE_USER_DEFAULT,
                                      DISPATCH_METHOD, &disp, &varResult, NULL,
                                      NULL);
      }
    } else {
      hr = E_INVALIDARG;
    }
  } catch (_com_error &e) {
    hr = e.Error();
  }

  return hr;
}

HRESULT ApiUtils::GetEdges(_variant_t &varRes, int queryType,
                           LPDISPATCH pGraphicDispatch) {
  HRESULT hr = NOERROR;
  try {
    if (pGraphicDispatch) {
      DISPID rgDispId[2] = {0};
      BSTR Names[2];
      Names[0] = SysAllocString(L"Edges");
      Names[0] = SysAllocString(L"EdgeType");

      hr = pGraphicDispatch->GetIDsOfNames(IID_NULL, Names, 1,
                                           LOCALE_USER_DEFAULT, rgDispId);
      SysFreeString(Names[0]);
      SysFreeString(Names[1]);

      if (SUCCEEDED(hr)) {
        ATL::CComVariant args[1];
        args[0].vt = VT_I4;
        args[0].lVal = queryType;

        _variant_t varResult;
        DISPPARAMS disp = {args, NULL, 1, 0};

        hr = pGraphicDispatch->Invoke(rgDispId[0], IID_NULL,
                                      LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
                                      &disp, &varResult, NULL, NULL);
      }
    } else {
      hr = E_INVALIDARG;
    }
  } catch (_com_error &e) {
    hr = e.Error();
  }

  return hr;
}

// 获得Type属性
HRESULT ApiUtils::GetLocatedGraphicType(LPDISPATCH pGraphicDispatch,
                                        int &nObjectType) {
  HRESULT hr = NOERROR;

  try {
    nObjectType = 0;

    if (pGraphicDispatch) {
      DISPID rgDispId = 0;
      OLECHAR name[] = L"Type";
      OLECHAR *Names[1] = {name};

      hr = pGraphicDispatch->GetIDsOfNames(IID_NULL, Names, 1,
                                           LOCALE_USER_DEFAULT, &rgDispId);

      if (SUCCEEDED(hr)) {
        VARIANT varResult;
        VariantInit(&varResult);
        V_VT(&varResult) = VT_I4;

        DISPPARAMS disp = {NULL, NULL, 0, 0};

        hr = pGraphicDispatch->Invoke(rgDispId, IID_NULL, LOCALE_USER_DEFAULT,
                                      DISPATCH_PROPERTYGET, &disp, &varResult,
                                      NULL, NULL);

        if (SUCCEEDED(hr)) {
          nObjectType = V_I4(&varResult);
        }
      }
    } else {
      hr = E_INVALIDARG;
    }
  } catch (_com_error &e) {
    hr = e.Error();
  }

  return hr;
}

geom_entity_type ApiUtils::GetGeomType(LPDISPATCH pDisp) {
  int objType;
  HRESULT hr = GetLocatedGraphicType(pDisp, (int &)objType);
  if (FAILED(hr)) {
    return geom_entity_type::vf_geom_none;
  }

  if (cs::igVertex == objType)
    return geom_entity_type::vf_vertex;
  if (cs::igEdge == objType)
    return geom_entity_type::vf_curve;
  if (cs::igFace == objType)
    return geom_entity_type::vf_face;
  if (cs::igBody == objType)
    return geom_entity_type::vf_body;
  if (cs::igEdges == objType)
    return geom_entity_type::vf_curves;
  if (cs::igFaces == objType)
    return geom_entity_type::vf_faces;

  return geom_entity_type::vf_geom_none;
}
// 通过ID获得对象类型
vfHRESULT ApiUtils::GetObjType(int &objType, long ObjID) {
  IDispatchPtr pEnt = FindEntityByID(ObjID);
  if (pEnt == nullptr) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  HRESULT hr = GetLocatedGraphicType(pEnt, (int &)objType);
  if (FAILED(hr)) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

// 枚举类型转换
geom_entity_type ApiUtils::ToEntityType(int objType) {
  if (cs::igVertex == objType || cs::igPoint2d == objType ||
      cs::igPoint3D == objType || cs::igIntersectionPointObject == objType) {
    return geom_entity_type::vf_vertex;
  } else if (cs::igArc2d == objType || cs::igArc3D == objType ||
             cs::igLine2d == objType || cs::igLine3D == objType ||
             cs::igEllipse2d == objType || cs::igCircle2d == objType ||
             cs::igCircle3D == objType || cs::igBsplineCurve2d == objType ||
             cs::igBSplineCurve3D == objType || cs::igEdge == objType) {
    return geom_entity_type::vf_curve;
  } else if (cs::igFace == objType) {
    return geom_entity_type::vf_face;
  } else if (cs::igBody == objType) {
    return geom_entity_type::vf_body;
  }
  return geom_entity_type::vf_geom_none;
}
static std::shared_ptr<PersistIDManager> s_pMng = nullptr;
IDispatchPtr ApiUtils::FindEntityByID(int64_t entID) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->FindEntityByID(entID);
}

IDispatchPtr ApiUtils::FindEntityByID(const char *entID) {
  int64_t id = std::stoull(entID);
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->FindEntityByID(id);
}

int64_t ApiUtils::GetEntityID(IDispatchPtr const &pEnt) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->GetEntityID(pEnt);
}

const char *ApiUtils::GetEntityIDString(IDispatchPtr const &pEnt) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  uint64_t id = pActiveDoc->GetEntityID(pEnt);
  if (UINT64_MAX == id) {
    return nullptr;
  }
  std::string idStr = std::to_string(id);
  return _strdup(idStr.c_str());
}

IDispatchPtr ApiUtils::FindFeatureByID(int64_t objID) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->FindObjectByID(-1 * objID);
}

IDispatchPtr ApiUtils::FindFeatureByID(const char *objID) {
  int64_t id = std::stoll(objID);
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->FindObjectByID(-1 * id);
}

int64_t ApiUtils::GetFeatureID(IDispatchPtr const &pEnt) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return -1 * pActiveDoc->GetObjectID(pEnt);
}

IDispatchPtr ApiUtils::EntityToGeomtry(IDispatchPtr const &pEnt,
                                       int64_t ptIDX) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return pActiveDoc->MapSketch3dGeometryToCurve(pEnt, ptIDX);
}

int64_t ApiUtils::GetEdgeSegmentID(IDispatchPtr const &pEdge) {
  pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  return -1 * pActiveDoc->GetEdgeSegmentID(pEdge);
}

bool PersistIDManager::GetReferenceKey(LPSAFEARRAY &pKeyArr,
                                       LPDISPATCH pEnt) const {
  int nObjType;
  HRESULT hr = ApiUtils::GetLocatedGraphicType(pEnt, nObjType);
  if (FAILED(hr)) {
    return false;
  }
  pKeyArr = nullptr;
  if (gm::igVertex == nObjType) {
    gm::VertexPtr pVtx = pEnt;
    hr = pVtx->GetReferenceKey(&pKeyArr);
    if (FAILED(hr) || pKeyArr == nullptr)
      return false;
  } else if (gm::igEdge == nObjType) {
    gm::EdgePtr pEdge = pEnt;
    hr = pEdge->GetReferenceKey(&pKeyArr);
    if (FAILED(hr) || pKeyArr == nullptr)
      return false;

  } else if (gm::igFace == nObjType) {
    gm::FacePtr pFace = pEnt;
    hr = pFace->GetReferenceKey(&pKeyArr);
    if (FAILED(hr) || pKeyArr == nullptr)
      return false;
  } else if (gm::igBody == nObjType) {
    gm::BodyPtr pBody = pEnt;
    hr = pBody->GetReferenceKey(&pKeyArr);
    if (FAILED(hr) || pKeyArr == nullptr)
      return false;
  } else {
    return false;
  }

  return true;
}

ATL::CComSafeArray<BYTE>
PersistIDManager::ByteArrToKey(std::vector<BYTE> const &vec) const {
  ATL::CComSafeArray<BYTE> keyArr(vec.size());
  for (size_t i = 0; i < vec.size(); i++) {
    keyArr.SetAt(i, vec[i]);
  }
  return keyArr;
}

std::vector<BYTE> PersistIDManager::KeyToByteArray(LPSAFEARRAY pKey) const {
  long lLBound, lUBound;
  SafeArrayGetLBound(pKey, 1, &lLBound);
  SafeArrayGetUBound(pKey, 1, &lUBound);
  std::vector<BYTE> byteArr(lUBound + 1);

  for (long i = lLBound; i <= lUBound; i++) {
    BYTE lValue;
    HRESULT hr = SafeArrayGetElement(pKey, &i, &lValue);
    if (SUCCEEDED(hr)) {
      byteArr[i] = lValue;
    }
  }
  return byteArr;
}

uint32_t PersistIDManager::GetEntityID(IDispatchPtr const &pEnt) {
  assert(pEnt != nullptr);
  // get referenctKey
  LPSAFEARRAY pKeyArr = nullptr;
  if (!GetReferenceKey(pKeyArr, pEnt)) {
    return UINT32_MAX;
  }
  std::vector<BYTE> byteArr = KeyToByteArray(pKeyArr);

  auto it = m_idMap.left.find(byteArr);
  if (it == m_idMap.left.end()) {
    uint32_t id = m_idMap.size();
    m_idMap.insert({byteArr, id});
    WriteToStream(byteArr, id);
    return id;
  } else {
    return it->second;
  }
}

IDispatchPtr PersistIDManager::FindEntityByID(uint32_t id) const {
  auto it = m_idMap.right.find(id);
  if (it == m_idMap.right.end()) {
    return nullptr;
  }
  std::vector<BYTE> const &byteArr = it->second;
  ATL::CComSafeArray<BYTE> keyArr = ByteArrToKey(byteArr);
  pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();

  LPSAFEARRAY pArr = keyArr.Detach();
  IDispatchPtr pDisp = nullptr;
  HRESULT hr = pDoc->BindKeyToObjectEx(true, &pArr, &pDisp.GetInterfacePtr());
  if (SUCCEEDED(hr) && pDisp != nullptr) {
    return pDisp;
  }

  return nullptr;
}

bool PersistIDManager::WriteToStream(std::vector<BYTE> const &key,
                                     uint32_t id) const {
  CString strStreamName;
  strStreamName.Format(L"%u", id);
  _bstr_t streamName = strStreamName.GetString();

  // 新建流，来存储信息
  IStream *pStream = nullptr;
  HRESULT hr = m_pStg->CreateStream(
      streamName, STGM_DIRECT | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
      0, 0, &pStream);

  size_t sz = key.size();
  ULONG pcbWritten = 0;
  hr = pStream->Write(&sz, sizeof(sz), &pcbWritten);
  assert(SUCCEEDED(hr));
  hr = pStream->Write(key.data(), sz, &pcbWritten);
  assert(SUCCEEDED(hr));
  hr = pStream->Commit(STGC_DEFAULT | STGC_OVERWRITE);
  assert(SUCCEEDED(hr));
  pStream->Release();

  hr = m_pAddInStg->Commit(STGC_DEFAULT | STGC_OVERWRITE);
  assert(SUCCEEDED(hr));
  IPersistStoragePtr pPersistStorage = SEAPP->GetActiveDocument();
  hr = pPersistStorage->Save(m_pAddInStg, false);
  assert(SUCCEEDED(hr));

  return true;
}

bool PersistIDManager::ReadFromStorage() {
  // 遍历流，每个流即为一个ID的描述
  IEnumSTATSTGPtr pEnum = nullptr;
  HRESULT hr = m_pStg->EnumElements(0, nullptr, 0, &pEnum);
  if (FAILED(hr))
    return false;

  ULONG fetched = 0;
  STATSTG stginfo;

  // 每次循环获取一个子存储或流的信息
  while (pEnum->Next(1, &stginfo, &fetched) == S_OK && fetched > 0) {
    IStream *pStream = nullptr;
    hr = m_pStg->OpenStream(stginfo.pwcsName, nullptr,
                            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
    assert(pStream != nullptr);
    const wchar_t *stgName = stginfo.pwcsName;
    wchar_t *endCharPtr = nullptr;
    uint32_t id = wcstoul(stgName, &endCharPtr, 10);
    size_t sz;
    pStream->Read(&sz, sizeof(sz), NULL);

    std::vector<BYTE> key(sz);
    pStream->Read(key.data(), sz, NULL);
    m_idMap.insert({key, id});
    // 释放 STATSTG 结构体中的内存
    if (stginfo.pwcsName != NULL)
      CoTaskMemFree(stginfo.pwcsName);
  }

  return true;
}

PersistIDManager::PersistIDManager(_bstr_t const &fileFullName,
                                   IStoragePtr const &pAddInStg,
                                   IStoragePtr const &pStg)
    : m_fileFullName(fileFullName), m_pAddInStg(pAddInStg), m_pStg(pStg) {
  assert(m_pAddInStg != nullptr);
  assert(m_pStg != nullptr);
}

bool PersistIDManager::GetManager(std::shared_ptr<PersistIDManager> &pMng,
                                  fw::SolidEdgeDocumentPtr pDoc) {
  assert(pDoc != nullptr);
  _bstr_t fullName = pDoc->GetFullName();
  const wchar_t *addInStgName = L"tgcomac";
  const wchar_t *stgName = fullName;

  if (nullptr == pMng || pMng->GetFullName() != fullName) {

    IStoragePtr pAddInStg = nullptr;
    IStoragePtr pStg = nullptr;
    if (ExistStorage(pAddInStg, pStg, addInStgName, pDoc)) {
      pMng = std::make_shared<PersistIDManager>(fullName, pAddInStg, pStg);
      return pMng->ReadFromStorage();
    }
    if (CreateStorage(pAddInStg, pStg, addInStgName, pDoc)) {
      pMng = std::make_shared<PersistIDManager>(fullName, pAddInStg, pStg);
      return true;
    }
    return false;
  }
  return true;
}

bool PersistIDManager::ExistStorage(IStoragePtr &pAddInStg, IStoragePtr &pStg,
                                    const wchar_t *addInStgName,
                                    fw::SolidEdgeDocumentPtr pDoc) {
  assert(pDoc != nullptr);
  pAddInStg = pDoc->GetAddInsStorage(addInStgName, STGM_DIRECT | STGM_READ |
                                                       STGM_SHARE_EXCLUSIVE);
  if (pAddInStg == nullptr)
    return false;

  HRESULT hr = pAddInStg->OpenStorage(
      addInStgName, nullptr, STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
      nullptr, 0, &pStg.GetInterfacePtr());
  if (FAILED(hr) || pStg == nullptr)
    return false;

  return true;
}

bool PersistIDManager::CreateStorage(IStoragePtr &pAddInStg, IStoragePtr &pStg,
                                     const wchar_t *addInStgName,
                                     fw::SolidEdgeDocumentPtr pDoc) {
  assert(pDoc != nullptr);
  pAddInStg = pDoc->GetAddInsStorage(addInStgName, STGM_DIRECT | STGM_CREATE |
                                                       STGM_READWRITE |
                                                       STGM_SHARE_EXCLUSIVE);
  if (pAddInStg == nullptr)
    return false;

  HRESULT hr = pAddInStg->CreateStorage(
      addInStgName,
      STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0,
      &pStg.GetInterfacePtr());
  if (FAILED(hr) || pStg == nullptr)
    return false;
  return true;
}

int ApiUtils::ConvertToFilterType(geom_entity_type geomType) {
  switch (geomType) {
  case geom_entity_type::vf_vertex:
    return int(SolidEdgeConstants::seLocateVertex);
  case geom_entity_type::vf_curve:
    return int(SolidEdgeConstants::seLocateEdge);
  case geom_entity_type::vf_face:
    return int(SolidEdgeConstants::seLocateFace);
  case geom_entity_type::vf_body:
    return int(SolidEdgeConstants::seLocateBody);
  default:
    return 0;
  }
  return 0;
}

//!
//! @brief 当前选择集中是否已有指定实体
//!
//! @param entityIDs 返回的符合正确类型的实体ID集
//! @param geoType   待查找的实体类型
//! @return vfHRESULT entityIDs非空时 Success，否则 Failure
//!
bool ApiUtils::HasEntityInSelectionSet(std::vector<int64_t> &entityIDs,
                                       int geoType) {

  fw::SelectSetPtr pSS = SEAPP->GetActiveSelectSet();
  if (pSS == nullptr || pSS->GetCount() == 0) {
    return false;
  }

  for (long i = 0; i < pSS->GetCount(); i++) {
    IDispatchPtr pEnt = pSS->Item(i + 1);
    geom_entity_type entType = ApiUtils::GetGeomType(pEnt);
    if (0 == (entType & geoType)) {
      continue;
    }

    int64_t entID = ApiUtils::GetEntityID(pEnt);
    if (entID != UINT64_MAX) {
      entityIDs.push_back(entID);
    }
  }

  return (!entityIDs.empty());
};

void ApiUtils::FindWpfWindows(HWND hwndParent,
                              std::unordered_set<HWND> *destWpfWindows) {

  // 枚举所有子窗口
  EnumChildWindows(
      hwndParent,
      [](HWND hwnd, LPARAM lParam) -> BOOL {
        std::unordered_set<HWND> *pPara =
            reinterpret_cast<std::unordered_set<HWND> *>(lParam);
        TCHAR szClsName[256];
        GetClassName(hwnd, szClsName, sizeof(szClsName));
        TCHAR szWindowName[256];
        std::wstring wstr(szClsName);

        if (wstr.starts_with(L"HwndWrapper")) {
          pPara->insert(hwnd);
        }

        // 递归查找子窗口
        ApiUtils::FindWpfWindows(hwnd, pPara);

        return TRUE; // 继续枚举
      },
      reinterpret_cast<LPARAM>(destWpfWindows));
}

fw::HighlightSetPtr ApiUtils::FindOrCreateFstHighlightSet(long color, int width,
                                                          bool forceCreateNew) {
  fw::SolidEdgeDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
  fw::HighlightSetsPtr pHiliSets = pActiveDoc->GetHighlightSets();
  long cnt = pHiliSets->GetCount();
  fw::HighlightSetPtr pRtHiliSet = nullptr;
  if (forceCreateNew || cnt == 0) {
    pRtHiliSet = pHiliSets->Add();

    // // 使显示为绿色，线宽4像素
    // // 高亮集，暂没有名字机制;这样会影响其他使用第一个高亮集的代码
    // // todo ，增加高亮集名字
    pRtHiliSet->PutColor(color);
    pRtHiliSet->PutWidth(width);
  } else {
    pRtHiliSet = pHiliSets->Item(1l);
  }

  return pRtHiliSet;
}

HRESULT ApiUtils::ThickenAndHighlight(fw::HighlightSetPtr const &pHiliSet,
                                      std::vector<int64_t> const &entIdArr) {

  for (auto entID : entIdArr) {
    IDispatchPtr pEnt = nullptr;
    if (entID < 0) {
      // 负数为特征ID
      pEnt = ApiUtils::FindFeatureByID(entID);

      pt::Sketch3DPtr pSketch3d = pEnt;
      if (pSketch3d != nullptr) {
        _bstr_t name = pSketch3d->GetName();
        int rosetteIdx;
        if (sscanf_s(name, "Rosette%d", &rosetteIdx)) {
          // 如此三维草图为Rosette坐标系，
          // 高亮他的所有边
          gm::EdgesPtr pRosetterEdges =
              pSketch3d->GetSketch3DFeature()->GetEdges(gm::igQueryAll);
          for (long i = 0; i < pRosetterEdges->GetCount(); i++) {
            gm::EdgePtr pEdge = pRosetterEdges->Item(i + 1);
            pHiliSet->AddItem(pEdge);
          }
        }
      }
      continue;
    } else {
      // 自然数为拓扑对象ID
      pEnt = ApiUtils::FindEntityByID(entID);
    }

    if (nullptr == pEnt) {
      return E_FAIL;
    }
    geom_entity_type geomType = ApiUtils::GetGeomType(pEnt);
    if (geomType == geom_entity_type::vf_faces) {
      // 为壳
      gm::FacesPtr pFaces = pEnt;
      auto cnt = pFaces->GetCount();
      for (long i = 0; i < pFaces->GetCount(); i++) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        pHiliSet->AddItem(pFace);
      }
    } else if (geomType == geom_entity_type::vf_curves) {
      // 为链
      gm::EdgesPtr pEdges = pEnt;
      auto cnt = pEdges->GetCount();
      for (long i = 0; i < pEdges->GetCount(); i++) {
        gm::EdgePtr pEdge = pEdges->Item(i + 1);
        pHiliSet->AddItem(pEdge);
      }
    } else {
      // 为普通单个拓扑对象或特征
      pHiliSet->AddItem(pEnt);
    }
  }
  pHiliSet->Draw();
  return S_OK;
}

HRESULT ApiUtils::ThickenAndHighlight(std::vector<int64_t> const &entIdArr,
                                      long color, int width,
                                      bool forceCreateNew) {
  if (entIdArr.empty()) {
    return S_OK;
  }
  fw::HighlightSetPtr pHiliSet =
      FindOrCreateFstHighlightSet(color, width, forceCreateNew);
  pHiliSet->PutColor(color);
  pHiliSet->PutWidth(width);
  ThickenAndHighlight(pHiliSet, entIdArr);
  return S_OK;
}

HRESULT
ApiUtils::ClearThickenAndHighlight(std::vector<int64_t> const &entIdArr) {
  if (entIdArr.empty()) {
    return S_OK;
  }

  // 建立映射
  fw::HighlightSetPtr pHiliSet = FindOrCreateFstHighlightSet();
  // pHiliSet->RemoveAll();
  // return S_OK;
  long cnt = pHiliSet->GetCount();
  std::map<IDispatchPtr, long> entIdxMap;
  for (long i = 0; i < cnt; i++) {
    IDispatchPtr pEnt = pHiliSet->Item(i + 1);
    if (pEnt == nullptr) {
      continue;
    }
    entIdxMap.emplace(pEnt, i + 1);
  }

  // 记录要删除的索引
  std::vector<long> removeIdxArr;
  for (auto entID : entIdArr) {
    IDispatchPtr pEnt = nullptr;
    if (entID < 0) {
      // 负数为特征ID
      pEnt = ApiUtils::FindFeatureByID(entID);

      pt::Sketch3DPtr pSketch3d = pEnt;
      if (pSketch3d != nullptr) {
        _bstr_t name = pSketch3d->GetName();
        int rosetteIdx;
        if (sscanf_s(name, "Rosette%d", &rosetteIdx)) {
          // 如此三维草图为Rosette坐标系，
          // 取消高亮他的所有边
          gm::EdgesPtr pRosetterEdges =
              pSketch3d->GetSketch3DFeature()->GetEdges(gm::igQueryAll);
          for (long i = 0; i < pRosetterEdges->GetCount(); i++) {
            gm::EdgePtr pEdge = pRosetterEdges->Item(i + 1);
            auto iter = entIdxMap.find(pEdge);
            if (iter != entIdxMap.end()) {
              removeIdxArr.push_back(iter->second);
            }
          }
        }
      }

    } else {
      // 自然数为拓扑对象ID
      pEnt = ApiUtils::FindEntityByID(entID);

      geom_entity_type geomType = ApiUtils::GetGeomType(pEnt);
      if (geomType == geom_entity_type::vf_faces) {
        // 为壳
        gm::FacesPtr pFaces = pEnt;
        for (long i = 0; i < pFaces->GetCount(); i++) {
          gm::FacePtr pFace = pFaces->Item(i + 1);
          auto iter = entIdxMap.find(pFace);
          if (iter != entIdxMap.end()) {
            removeIdxArr.push_back(iter->second);
          }
        }
      } else if (geomType == geom_entity_type::vf_curves) {
        // 为链
        gm::EdgesPtr pEdges = pEnt;
        for (long i = 0; i < pEdges->GetCount(); i++) {
          gm::EdgePtr pEdge = pEdges->Item(i + 1);
          auto iter = entIdxMap.find(pEdge);
          if (iter != entIdxMap.end()) {
            removeIdxArr.push_back(iter->second);
          }
        }
      } else {
        // 为普通单个拓扑对象或特征
        auto iter = entIdxMap.find(pEnt);
        if (iter != entIdxMap.end()) {
          removeIdxArr.push_back(iter->second);
        }
      }
    }
  }

  // 避免删除时，索引错乱,从大到小排序
  std::sort(removeIdxArr.begin(), removeIdxArr.end(), std::greater<long>());
  for (auto idx : removeIdxArr) {
    try {
      pHiliSet->RemoveItem(idx);
    } catch (_com_error const &e) {
      int aaaa = 0;
    }
  }

  int remainCnt = pHiliSet->GetCount();
  pHiliSet->Draw();
  return S_OK;
}

IDispatchPtr ApiUtils::Sketch3dTopoToObject(gm::EdgePtr pEdge) {
  int64_t cvId = ApiUtils::GetEdgeSegmentID(pEdge);
  IDispatchPtr pCv = ApiUtils::FindFeatureByID(cvId);
  return pCv;
}

// 计算向量模长
double ApiUtils::magnitude(const double vec[3]) {
  return std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
}

// 归一化向量（结果写入 result 数组）
void ApiUtils::normalized(const double vec[3], double result[3]) {
  double mag = magnitude(vec);
  if (mag == 0)
    throw std::runtime_error("Cannot normalize zero vector");
  result[0] = vec[0] / mag;
  result[1] = vec[1] / mag;
  result[2] = vec[2] / mag;
}

// 计算点积
double ApiUtils::dot(const double v1[3], const double v2[3]) {
  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}

// 计算叉乘（结果写入 result 数组）
void ApiUtils::cross(const double v1[3], const double v2[3], double result[3]) {
  result[0] = v1[1] * v2[2] - v1[2] * v2[1];
  result[1] = v1[2] * v2[0] - v1[0] * v2[2];
  result[2] = v1[0] * v2[1] - v1[1] * v2[0];
}

// 方法1: 叉乘法找垂直向量（结果写入 result 数组）
void ApiUtils::findPerpendicularCross(const double vec[3], double result[3]) {
  const double baseVectors[3][3] = {
      {1, 0, 0}, // i
      {0, 1, 0}, // j
      {0, 0, 1}  // k
  };

  for (int i = 0; i < 3; ++i) {
    double tempResult[3];
    cross(vec, baseVectors[i], tempResult);
    if (magnitude(tempResult) > 1e-6) { // 排除零向量
      normalized(tempResult, result);
      return;
    }
  }
  // throw std::runtime_error("Input vector is zero vector");
}

// 函数：两个三维向量相加
void ApiUtils::vectorAdd(const double a[3], const double b[3],
                         double result[3]) {
  // 逐分量相加
  for (int i = 0; i < 3; ++i) {
    result[i] = a[i] + b[i];
  }
}

bool ApiUtils::GetEdgeLength(gm::EdgePtr pEdge, double &length) {

  if (pEdge == nullptr)
    return false;
  double minParam, maxParam;
  if (FAILED(pEdge->GetParamExtents(&minParam, &maxParam))) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return false;
  }

  if (FAILED(pEdge->GetLengthAtParam(minParam, maxParam, &length))) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return false;
  }
  return true;
}

gm::FacePtr ApiUtils::FindPointOnFace(double x, double y, double z,
                                      gm::FacesPtr pFaces) {
  long faceCnt = pFaces->GetCount();
  ATL::CComSafeArray<double> aPoint = ToComSafeArray({x, y, z});
  for (long i = 0; i < faceCnt; i++) {
    gm::FacePtr pFace = pFaces->Item(i + 1);
    long bOn;
    ATL::CComSafeArray<double> uv(2);
    double closestDis;
    ATL::CComSafeArray<double> closestPoint(3);
    pFace->IsPointOn(aPoint.GetSafeArrayPtr(), &bOn, uv.GetSafeArrayPtr(),
                     &closestDis, closestPoint.GetSafeArrayPtr());
    if (bOn) {
      return pFace;
    }
  }
  return nullptr;
}

std::vector<int64_t> ApiUtils::CollectIds(const char *inputStringValue[]) {
  std::vector<int64_t> ids;
  while (*inputStringValue) {
    std::string idStr = *inputStringValue;
    if (idStr.empty()) {
      inputStringValue++;
      continue;
    }
    int64_t topoId = std::stoull(idStr);
    ids.push_back(topoId);
    inputStringValue++;
  }
  return ids;
}

void ApiUtils::DelFeatureRecursive(pt::PartDocumentPtr const &pPartDoc,
                                   IDispatchPtr const &pObj) {
  try {
    if (pObj == nullptr) {
      return;
    }
    LPSAFEARRAY pParent = nullptr;
    LPSAFEARRAY pChildren = nullptr;
    pPartDoc->GetParentsAndDependents(pObj.GetInterfacePtr(), &pParent,
                                      &pChildren);

    // int typeId;
    // GetLocatedGraphicType(pObj, typeId);
    if (nullptr != pParent) {
      ATL::CComSafeArray<LPDISPATCH> parents;
      parents.Attach(pParent);
      for (int i = 0; i < parents.GetCount(); i++) {
        DelFeatureRecursive(pPartDoc, IDispatchPtr{parents[i]});
      }
    }
    if (nullptr != pChildren) {
      SafeArrayDestroy(pChildren);
    }
    HRESULT hr = InvokeDelete(pObj);
  } catch (_com_error const &e) {
  }
}

gm::EdgesPtr ApiUtils::ExtractTopoEdges(LPSAFEARRAY pCvDispArr) {

  try {
    ATL::CComSafeArray<IDispatch *> cvArr;
    cvArr.Attach(pCvDispArr);
    if (cvArr.GetCount() == 0) {
      return nullptr;
    }
    gm::EdgePtr pFstEdge = ApiUtils::EntityToGeomtry(IDispatchPtr(cvArr[0]));
    gm::BodyPtr pBody = pFstEdge->GetBody();
    gm::EdgesPtr pEdges = pBody->CreateCollection(
        gm::TopologyCollectionTypeConstants::seEdgeCollection);
    pEdges->Add(pFstEdge);
    for (int i = 1; i < cvArr.GetCount(); i++) {
      gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(IDispatchPtr(cvArr[i]));
      pEdges->Add(pEdge);
    }
    return pEdges;
  } catch (_com_error const &e) {
    return nullptr;
  }
}

} // namespace VFAdapter